import { renderLiquid } from './liquid/index'
import { renderMarkdown, renderUnified } from './unified/index'
import { engine } from './liquid/engine'
import type { Context } from '@/types'

interface RenderOptions {
  cache?: boolean | ((template: string, context: Context) => string)
  filename?: string
  textOnly?: boolean
}

const globalCache = new Map<string, string>()

// parse multiple times because some templates contain more templates. :]
export async function renderContent(
  template = '',
  context: Context = {} as Context,
  options: RenderOptions = {},
): Promise<string> {
  // If called with a falsy template, it can't ever become something
  // when rendered. We can exit early to save some pointless work.
  if (!template) return template
  let cacheKey: string | null = null
  if (options && options.cache) {
    if (!context) throw new Error("If setting 'cache' in options, the 'context' must be set too")
    if (typeof options.cache === 'function') {
      cacheKey = options.cache(template, context)
    } else {
      cacheKey = getDefaultCacheKey(template, context)
    }
    if (cacheKey && typeof cacheKey !== 'string') {
      throw new Error('cache option must return a string if truthy')
    }
    if (globalCache.has(cacheKey)) {
      return globalCache.get(cacheKey) as string
    }
  }
  try {
    template = await renderLiquid(template, context)
    if (context.markdownRequested) {
      const md = await renderMarkdown(template, context)

      return md
    }

    const html = await renderUnified(template, context, options)
    if (cacheKey) {
      globalCache.set(cacheKey, html)
    }
    return html
  } catch (error) {
    if (options.filename) {
      console.error(`renderContent failed on file: ${options.filename}`)
    }
    throw error
  }
}

function getDefaultCacheKey(template: string, context: Context): string {
  return `${template}:${context.currentVersion}:${context.currentLanguage}`
}

export const liquid = engine
